﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using Microsoft.Synchronization.Data;
using System.Data;
using log4net;
using SyncLib.Client;
using SyncLib.Data;
using System.IO;
using Microsoft.Synchronization;

namespace SyncLib.Provider
{
    public class ServerComplexSync : BaseKeeperSync
    {
        ILog Log = LogManager.GetLogger(typeof(Log4NetImplement));
        protected override void OnChangesSelected(Microsoft.Synchronization.Data.DbChangesSelectedEventArgs e)
        {
            try
            {
                base.OnChangesSelected(e);
                Transaction = (SqlTransaction)e.Transaction;
                SelectDependencyData(e);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                Transaction = null;
            }
        }
        protected override void OnSyncProgress(DbSyncProgressEventArgs e)
        {
            try
            {
                Log.Debug(
                         string.Format("SessionId={0} {1} SyncProgress: State={2} Table ={3} Rowindex={4}/Count={5}",
                         SessionId, Context.CurrentScope.ScopeName, e.Stage, e.TableProgress.TableName, e.TableProgress.RowIndex, e.TableProgress.DataTable.Rows.Count));

                switch (e.Stage)
                {
                    case DbSyncStage.ApplyingDeletes:
                    case DbSyncStage.ApplyingInserts:
                    case DbSyncStage.ApplyingUpdates:
                        {                            
                            if (e.TableProgress.DataTable.Rows.Count > e.TableProgress.RowIndex)
                            {
                                DataView view = new DataView(e.TableProgress.DataTable);
                                view.RowStateFilter = DataViewRowState.CurrentRows | DataViewRowState.Deleted;
                                DataRowView rowView = view[e.TableProgress.RowIndex];
                                if (!rowView.Row.Equals(e.TableProgress.DataTable.Rows[e.TableProgress.RowIndex]))
                                {
                                    Log.Info("RowIndex not match dataview--> Manunal localtion rowview");
                                    rowView = null;

                                    foreach (DataRowView rowv in view)
                                        if (rowv.Row.Equals(e.TableProgress.DataTable.Rows[e.TableProgress.RowIndex]))
                                        {
                                            rowView = rowv;
                                            break;
                                        }
                                }
                                MonitorEvent.Monitor.Info(string.Format("Lưu thay đổi bảng con {0} {1}/{2}", e.TableProgress.TableName, e.TableProgress.RowIndex + 1, e.TableProgress.DataTable.Rows.Count));
                                ApplyDependencyTables(rowView, e);


                            }
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
                Log.Fatal(
                   string.Format("SessionId={0} {1} SyncProgress: State={2} Table ={3} Rowindex={4}",
                   SessionId, Context.CurrentScope.ScopeName, e.Stage, e.TableProgress.TableName, e.TableProgress.RowIndex),
                       ex);
                if (_Transaction != null)
                    _Transaction.Rollback();
                throw ex;
            }
        }

        private void SelectDependencyData(Microsoft.Synchronization.Data.DbChangesSelectedEventArgs e)
        {
            foreach (DataTable tblData in e.Context.DataSet.Tables)
                SelectDependencyData(tblData.TableName, e);
        }

        void SelectDependencyData(string tableName, DbChangesSelectedEventArgs e)
        {
            string debugStep = "Unknown";
            try
            {
                Log.Info("GetDependencyData for table " + tableName);
                TableOfScope tos = _SyncContext.CurrentScope.FindTable(tableName);
                if (tos != null)
                {
                    _SyncContext.CurrentTable = tos;
                    if (tos.DependencyTables.Length > 0)
                    {
                        DataView view = new DataView(e.Context.DataSet.Tables[tableName]);
                        view.RowStateFilter = DataViewRowState.CurrentRows | DataViewRowState.Deleted;

                        for (int rowIndex = 0; rowIndex < view.Count; rowIndex++)
                        {
                            //MonitorEvent.Monitor.Info(string.Format("{2}({0}/{1}) ", rowIndex + 1, view.Count, tableName));
                            debugStep = "Get primary columns values of processRow";
                            string[] primaryColumns = tos.GetPrimaryColumns();
                            List<string> primaryValues = new List<string>();
                            for (int i = 0; i < primaryColumns.Length; i++)
                            {
                                primaryValues.Add(view[rowIndex][primaryColumns[i]].ToString());
                            }
                            string[] arrPrimaryValues = primaryValues.ToArray();
                            primaryValues.Clear();
                            primaryValues = null;

                            KDataBatchProducer batchProducer = new KDataBatchProducer(tableName, Connection, Transaction, ClientId, _SyncContext.Position.ToString());
                            batchProducer.BatchSize = _SyncContext.CurrentScope.BatchSize;
                            batchProducer.DelegateSerializeBatch = new SerializeBatch(batchProducer.SaveBatchToSQL);
                            batchProducer.BatchDirectory = CommonVariable.SyncFolderBatch + "\\" + ClientId.ToString();
                            string dataKey = KeeperSyncUtility.CreateDependencyKey(tos.TableName, rowIndex, arrPrimaryValues);
                            batchProducer.PrefixFileName = dataKey;
                            for (int i = 0; i < tos.DependencyTables.Length; i++)
                            {

                                DependencyTable childrenTable = tos.DependencyTables[i];
                                _SyncContext.CurrentDepen = childrenTable;
                                //  MonitorEvent.Monitor.Info(string.Format("{2}({0}/{1})==> ", rowIndex + 1, view.Count, tableName, childrenTable.TableName));

                                SelectDependencyData(childrenTable, view[rowIndex], e, batchProducer);
                                debugStep = "Upload DependencyData";
                                // UploadKDataBatchHandle(dataKey, batchHandle);
                                _SyncContext.CurrentDepen = null;
                            }
                            batchProducer.EndBatchProducer();


                        }
                    }
                }
                Log.Info("End GetDependencyData for table " + tableName);
            }
            catch (Exception ex)
            {
                Log.Fatal("End GetDependencyData for table " + tableName + " with exception(" + debugStep + "):", ex);
                throw ex;
            }
            finally
            {
                _SyncContext.CurrentTable = null;
                _SyncContext.CurrentDepen = null;
            }
        }
        void SelectDependencyData(DependencyTable childrenTable, DataRowView currentRow, DbChangesSelectedEventArgs e, KDataBatchProducer batchProducer)
        {
            Log.InfoFormat(" Select dependency rows of table {0} in table {1}", _SyncContext.CurrentTable.TableName, childrenTable.TableName);

            using (SqlCommand command = new SqlCommand(childrenTable.SelectRowCommand, Connection, Transaction))
            {
                command.CommandType = CommandType.StoredProcedure;
                command.Parameters.AddRange(
                    SqlHelperParameterCache.GetSpParameterSet(CommonVariable.SqlConnectionString,
                            command.CommandText)
                    );
                KeeperSyncUtility.AssignParameterValues(command, currentRow, DbSyncStage.SelectingChanges, Context);
                using (SqlDataReader reader = command.ExecuteReader())
                {
                    batchProducer.AddReader(reader, childrenTable.TableName);

                }
            }
        }
        void ApplyDependencyTables(DataRowView processRow, DbSyncProgressEventArgs e)
        {
            string debugStep = "Unknown";
            try
            {
                debugStep = "Find TableOfScope";
                TableOfScope tos = _SyncContext.CurrentScope.FindTable(e.TableProgress.TableName);
                if (tos == null)
                {
                    Log.WarnFormat("ApplyDependencyTables[Skipped] TableOfScope {0} not found in {1}",
                        e.TableProgress.TableName, _SyncContext.CurrentScope.ScopeName);
                    return;
                }
                _SyncContext.CurrentTable = tos;

                debugStep = "Get primary columns values of processRow";
                string[] primaryColumns = tos.GetPrimaryColumns();
                List<string> primaryValues = new List<string>();
                for (int i = 0; i < primaryColumns.Length; i++)
                {
                    primaryValues.Add(processRow[primaryColumns[i]].ToString());
                }
                string[] arrPrimaryValues = primaryValues.ToArray();
                primaryValues.Clear();
                primaryValues = null;

                foreach (DependencyTable detailTable in tos.DependencyTables)
                {
                    _SyncContext.CurrentDepen = detailTable;
                    KeeperSyncUtility.DeTPreApplyTableCommand(processRow, e.Stage, this);
                }

                string fileKey = KeeperSyncUtility.CreateDependencyKey(tos.TableName, e.TableProgress.RowIndex, arrPrimaryValues);
                int fileIndex = 0;
                KDataBatch batch = KDataBatchFactory.DeserializeFromSql(fileKey + "_" + fileIndex.ToString() + ".depen", Connection, Transaction, ClientId, SyncProviderPosition.Local.ToString());
                while (batch != null)
                {

                    DataSet depenData = batch.DataSetSurrogate.ConvertToDataSet();
                    foreach (DataTable table in depenData.Tables)
                    {
                        if (table.Rows.Count > 0)
                        {
                            _SyncContext.CurrentDepen = tos.FindDependency(table.TableName);
                            int count = KeeperSyncUtility.DeTCallApplyRowCommand(table, e.Stage, this);
                            DbSyncTableProgress tableProcess = null;
                            if (DependencyStatistic.ContainsKey(tos.TableName))
                            {
                                DbSyncScopeProgress tosProcesss = DependencyStatistic[tos.TableName];
                                tableProcess = tosProcesss.FindTableProgress(_SyncContext.CurrentDepen.TableName);

                                if (tableProcess == null)
                                {
                                    tableProcess = new DbSyncTableProgress(_SyncContext.CurrentDepen.TableName);
                                    tosProcesss.TablesProgress.Add(tableProcess);
                                }
                            }
                            else
                            {
                                DbSyncScopeProgress tosProcess = new DbSyncScopeProgress();
                                tableProcess = new DbSyncTableProgress(_SyncContext.CurrentDepen.TableName);
                                tosProcess.TablesProgress.Add(tableProcess);
                                DependencyStatistic.Add(tos.TableName, tosProcess);
                            }
                            tableProcess.ChangesApplied += count;
                            tableProcess.Inserts += count;
                            Log.DebugFormat("        End apply batch {0}. Total applie {1}", fileIndex, count);
                        }
                        _SyncContext.CurrentDepen = null;
                    }
                    fileIndex++;

                    batch = KDataBatchFactory.DeserializeFromSql(fileKey + "_" + fileIndex.ToString() + ".depen", Connection, Transaction, ClientId, SyncProviderPosition.Local.ToString());

                }

                KeeperSyncUtility.TOSAfterApplyCommand(processRow, e.Stage, this);
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("ApplyDependencyTables {0}\n {1} \n[Step info]{2}",
                        e.TableProgress.TableName, _SyncContext.GetCurrentInfo(), debugStep), ex);
            }
            finally
            {
                _SyncContext.CurrentTable = null;
                _SyncContext.CurrentDepen = null;
            }

        }
        void SaveDataBatchToSql(KDataBatch batch)
        {
        }
    }
}
